/*
 * OREKIT-X
 * Copyright 2002-2008 CS Communication & Systemes
 * 
 * Parts of this software package have been licensed to CS
 * Communication & Systemes (CS) under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work for
 * additional information.
 *  
 * This is an experimental copy of OREKIT from www.orekit.org.
 * Please use the original OREKIT from orekit.org for normal work
 * unrelated to this research project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You
 * may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package ore.bodies;

import ore.Frame;
import ore.PositionVelocity;
import ore.Transform;
import ore.errors.OrekitException;
import ore.time.AbsoluteDate;

/** 
 * Abstract implementation of the {@link CelestialBody} interface.
 * 
 * This abstract implementation provides basic services that can be
 * shared by most implementations of the {@link CelestialBody}
 * interface. It holds the attraction coefficient and the
 * body-centered frame, and also provides a way to build this frame
 * automatically if not already provided by sub-classes.  The only
 * method that still needs to be implemented by sub-classes is the
 * {@link #getPositionVelocity(AbsoluteDate, Frame)
 * getPositionVelocity} method.
 * 
 * @author Luc Maisonobe
 */
public abstract class AbstractCelestialBody
    extends Object
    implements ore.CelestialBody
{

    /**
     * Attraction coefficient of the body (m<sup>3</sup>/s<sup>2</sup>).
     */
    private final double gm;
    /** 
     * Inertial body-centered frame.
     */
    private final Frame bodyCenteredFrame;


    /** 
     * @param gm attraction coefficient (in m<sup>3</sup>/s<sup>2</sup>)
     * @param frame existing body-centered frame to use
     */
    protected AbstractCelestialBody(double gm, Frame frame) {
        super();
        this.gm = gm;
        this.bodyCenteredFrame = frame;
    }
    /** 
     * The underlying body-centered frame built is a direct child of
     * EME2000. Its defining transform is a simple linear transform
     * without any rotation. The frame axes are therefore always
     * parallel to the EME2000 frame axes.
     * 
     * @param gm Attraction coefficient (in
     * m<sup>3</sup>/s<sup>2</sup>)
     * @param frameName Frame name to use
     * @param definingFrame Frame in which celestial body coordinates
     * are defined
     */
    protected AbstractCelestialBody(double gm, String frameName, Frame definingFrame){
        super();
        this.gm = gm;
        this.bodyCenteredFrame = new BodyCenteredFrame(frameName, definingFrame);
    }



    public double getGM() {
        return this.gm;
    }
    public Frame getFrame() {
        return this.bodyCenteredFrame;
    }
    /** {@inheritDoc}
     */
    public abstract PositionVelocity getPositionVelocity(AbsoluteDate date, Frame frame)
        throws OrekitException;

    /** 
     * Inertially oriented body-centered frame aligned with EME2000.
     */
    private class BodyCenteredFrame
        extends Frame
    {
        public BodyCenteredFrame(String name, Frame definingFrame) {
            super(definingFrame, null, name);
        }


        /** {@inheritDoc}
         */
        protected void updateFrame(AbsoluteDate date) throws OrekitException {
            /*
             * Compute position velocity with respect to parent frame
             */
            PositionVelocity pv = AbstractCelestialBody.this.getPositionVelocity(date, this.getParent());
            /*
             * Update transform from parent to self
             */
            this.setTransform(new Transform(pv.getPosition().negate(),
                                            pv.getVelocity().negate()));
        }
    }
}
